package org.treblereel.gwt.widget.client; import java.util.LinkedHashMap; import java.util.Map.Entry; import org.treblereel.gwt.widget.client.styles.TreeGridResources; import com.google.gwt.cell.client.FieldUpdater; import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.google.gwt.user.cellview.client.CellTable; import com.google.gwt.user.cellview.client.CellTable.Resources; import com.google.gwt.user.cellview.client.Column; import com.google.gwt.user.cellview.client.TextColumn; import com.google.gwt.user.cellview.client.TextHeader; import com.google.gwt.user.client.ui.ScrollPanel; import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.view.client.ListDataProvider; import com.google.gwt.view.client.MultiSelectionModel; import com.google.gwt.view.client.RowCountChangeEvent; import com.google.gwt.view.client.RowCountChangeEvent.Handler; import com.google.gwt.view.client.SelectionModel; /** * * @author chani * */ public class TreeGrid<T extends TreeNode<T>> extends VerticalPanel { private class TreeGridWrapper<T extends TreeNode<T>> extends CellTable<T> { private ListDataProvider<T> dataProvider; private static final int DEFAULT_PAGESIZE = 15; public TreeGridWrapper(Resources resources) { super(DEFAULT_PAGESIZE, resources); init(); setStyleName("treeGrid"); } @Override public void addColumn(Column<T, ?> column) { super.addColumn(column); } public void addExpandableColumn(ExpandableColumn<T> expandableColumn) { insertColumn(0, expandableColumn); setFieldUpdater(expandableColumn); expandableColumn.setCellStyleNames("treeGrid-ExpandableColumn"); } private void addExpandableColumnAction(int index, T object, String value) { if (!object.isLeaf() && !object.isExpand()) { expandNode(index, object); } else if (!object.isLeaf() && object.isExpand()) { collapseNode(object); } } private void collapseNode(T object) { object.setExpand(false); for (T child : object.getChildren()) { if (child.getChildren().size() > 0) { collapseNode(child); } this.dataProvider.getList().remove(child); } this.setPageSize(this.dataProvider.getList().size()); } private void expandNode(int index, T object) { if (!object.isExpand() && !object.isLeaf()) { object.setExpand(true); int i = index; for (T child : object.getChildren()) { i++; this.dataProvider.getList().add(i, child); } this.setPageSize(this.dataProvider.getList().size()); } } public ListDataProvider<T> getDataStore() { return this.dataProvider; } private void init() { initDataStore(); } private void initDataStore() { dataProvider = new ListDataProvider<T>(); dataProvider.addDataDisplay(this); } public void setSelectable(boolean action) { treeGrid.setSelectable(action); } public void setColumnLines(boolean result) { if (result) { addStyleName("cellBorder"); } else { removeStyleName("cellBorder"); } } private void setRowSelected(T object, boolean selection) { getSelectionModel().setSelected(object, selection); if (getSelectionModel() instanceof MultiSelectionModel) { if (object.getChildren().size() > 0) { object.setChildrenSelected(selection); for (T o : object.getChildren()) { setRowSelected(o, selection); } } } } private void setFieldUpdater(final ExpandableColumn<T> expandableColumn) { expandableColumn.setFieldUpdater(new FieldUpdater<T, String>() { public void update(int index, T object, String value) { if (value.equals("ICON_CHECKBOX") && isSelectable()) { setRowSelected(object, !getSelectionModel().isSelected(object)); checkParentChildrenSelected(object.getParent()); treeGrid.redraw(); } else { addExpandableColumnAction(index, object, value); } } }); } private void checkParentChildrenSelected(T object) { int selectedCounter = 0; boolean preventToBecomeisChidlrenSelected = false; for (T neighbor : object.getChildren()) { if (getSelectionModel().isSelected(neighbor) == true) selectedCounter++; if(neighbor.getChildren().size() > 0 && !neighbor.isChidlrenSelected()){ preventToBecomeisChidlrenSelected = true; } } if (selectedCounter == object.getChildren().size()) { object.setChildrenSelected(true); getSelectionModel().setSelected(object, true); } else if (selectedCounter < object.getChildren().size() && selectedCounter != 0) { object.setChildrenSelected(false); getSelectionModel().setSelected(object, true); } else if (selectedCounter == 0) { object.setChildrenSelected(false); getSelectionModel().setSelected(object, false); } if(preventToBecomeisChidlrenSelected){ object.setChildrenSelected(false); } if (object.getLevel() != 0) checkParentChildrenSelected(object.getParent()); } public void expandAll(boolean action) { for (T o : dataProvider.getList()) { if (action == false) { collapseNode(o); } else { expandAllAction(treeGrid.getVisibleItems().indexOf(o), o); } } } private void expandAllAction(int index, T object) { expandNode(index, object); if (object.getChildren().size() > 0) { for (T child : object.getChildren()) { expandAllAction(treeGrid.getVisibleItems().indexOf(child), child); } } } } private ScrollPanel scrollPanel; private TreeGridWrapper<T> treeGrid; private CellTable<T> header; private TextColumn<T> offSetColumn; private LinkedHashMap<Integer, Column<T, ?>> columns = new LinkedHashMap<Integer, Column<T, ?>>(); private LinkedHashMap<Column<T, ?>, String> columnNames = new LinkedHashMap<Column<T, ?>, String>(); private boolean selectable = false; private TextHeader offSetHeader; private Resources resources; public TreeGrid() { init(); } public TreeGrid(Resources resources) { this.resources = resources; init(); } public void addColumn(Column<T, ?> column, String name) { columns.put(columns.size() + 1, column); columnNames.put(column, name); refreshColumns(); } public void addExpandableColumn(Column<T, ?> column, String name) { columns.put(0, column); columnNames.put(column, name); refreshColumns(); } /** * Add offSet column on TreeGrid resize if scroll appears, remove if scroll * is not exist and do nothing if offSet column already exist. */ private void addOffserColumn(boolean action) { if (action) { if (header.getColumnIndex(offSetColumn) == -1) { header.setColumnWidth(offSetColumn, calculateOffsetColumnWidth() + "px"); header.addColumn(offSetColumn, offSetHeader); } } else { if (header.getColumnIndex(offSetColumn) != -1) header.removeColumn(offSetColumn); } } private int calculateOffsetColumnWidth() { int treeGridWidth = treeGrid.getOffsetWidth(); int scrollPanelWidth = scrollPanel.getOffsetWidth(); return scrollPanelWidth - treeGridWidth; } public void collapseAll() { treeGrid.expandAll(false); } public void expandAll() { treeGrid.expandAll(true); } private void calculatePanelSize() { int hSize = getBody().getOffsetHeight() - header.getOffsetHeight(); if (hSize <= treeGrid.getBodyHeight()) { addOffserColumn(true); } else { addOffserColumn(false); } } private void createOffsetColumn() { offSetColumn = new TextColumn<T>() { @Override public String getValue(T object) { return null; } }; offSetHeader = new TextHeader(""); offSetHeader.setHeaderStyleNames("treeGridOffsetHeader"); } public ListDataProvider<T> getDataStore() { return treeGrid.dataProvider; } private void init() { initHeader(); initTreeGrid(); } private void initHeader() { header = new CellTable<T>(); header.setVisible(false); header.setRowCount(0, true); add(header); createOffsetColumn(); } private void initTreeGrid() { if (resources == null) { TreeGridResources.INSTANCE.cellTableStyle().ensureInjected(); treeGrid = new TreeGridWrapper<T>(TreeGridResources.INSTANCE); } else { treeGrid = new TreeGridWrapper<T>(resources); } treeGrid.addRowCountChangeHandler(createRowCountChangeHandler()); scrollPanel = new ScrollPanel(); scrollPanel.add(treeGrid); this.add(scrollPanel); } public void setAlwaysShowScrollBars(boolean show) { scrollPanel.setAlwaysShowScrollBars(show); } private Handler createRowCountChangeHandler() { return new RowCountChangeEvent.Handler() { public void onRowCountChange(RowCountChangeEvent event) { Scheduler.get().scheduleDeferred(new ScheduledCommand() { public void execute() { calculatePanelSize(); } }); } }; } @SuppressWarnings("unchecked") private void refreshColumns() { for (int i = 0; i < treeGrid.getColumnCount(); i++) { treeGrid.removeColumn(i); header.removeColumn(i); } for (Entry<Integer, Column<T, ?>> kv : columns.entrySet()) { if (kv.getValue() instanceof ExpandableColumn) { treeGrid.addExpandableColumn((ExpandableColumn<T>) kv .getValue()); header.addColumn(kv.getValue(), columnNames.get(kv.getValue())); } else { treeGrid.addColumn(kv.getValue()); header.addColumn(kv.getValue(), columnNames.get(kv.getValue())); } } } public void removeColumn(Column<T, ?> column) { columns.remove(column); columnNames.remove(column); refreshColumns(); } public void setColumnLines(boolean result) { treeGrid.setColumnLines(result); } public void setColumnWidth(Column<T, ?> column, String width) { treeGrid.setColumnWidth(column, width); header.setColumnWidth(column, width); } public com.google.gwt.event.shared.HandlerRegistration addAttachHandler( com.google.gwt.event.logical.shared.AttachEvent.Handler handler) { return treeGrid.addAttachHandler(handler); } public void setColumnWidth(Integer columnIndex, String width) { treeGrid.setColumnWidth(columnIndex, width); header.setColumnWidth(columnIndex, width); } public void setHeight(String height) { super.setHeight(height); scrollPanel.setHeight(height); } public void setSelectable(boolean action) { this.selectable = action; } public boolean isSelectable() { return this.selectable; } public void redraw() { treeGrid.redraw(); } public void setSelectionMode() { } public void setWidth(String width) { super.setWidth(width); scrollPanel.setWidth(width); Integer widthInteger = Integer.parseInt(width.replace("px", "")); header.setWidth(String.valueOf((widthInteger - scrollPanel .getOffsetWidth())) + "px"); } public void showHeader(Boolean show) { header.setVisible(show); header.setStylePrimaryName("treeGrid treeGridHeaderBody"); } public void showScroll(boolean show) { scrollPanel.setAlwaysShowScrollBars(show ? true : false); } public void setSelectionModel(SelectionModel<T> sm) { treeGrid.setSelectionModel(sm, null); } @SuppressWarnings("unchecked") public SelectionModel<T> getSelectionModel() { return (SelectionModel<T>) treeGrid.getSelectionModel(); } }